Editor Guide
Editor Overview
A quick introduction to basic editor concepts.
Game Structure
The whole game is divided into layouts, each composed of multiple layers. Both layouts and layers are user-created.
Layers are individual building blocks of the game: images, animations, sounds, texts, etc.
Layouts are collections of layers that serve a particular function. In particular, screen layouts describe specific views of the game. Only one screen layout can be loaded at a time. Non-screen layouts can be added to screen layouts via component layers.
Example setup: A screen layout named
mainScreenis loaded. It contains layers describing the background image and a clickable button. It also contains a component layer configured to point to a non-screen layout calledpopup. The popup layout contains layers describing the popup container, texts, and a close button. When the player presses the button onmainScreen, thepopuplayout is shown. When the player presses the close button, thepopuplayout is hidden.
Layouts also support inheritance. For example, the mobile main screen layout can inherit from the desktop main screen layout. Thanks to this, the configuration of these two layouts becomes linked, and all changes in the desktop layout will be carried over to the mobile layout (but not the other way around).
Event System
All communication between editor layers and layouts is handled by the event system. The event system can be divided into three parts:
-
event emitters - elements that send events; for example, a mouse click or an animation reaching its end.
-
event triggers - elements that receive events; for example, an animation waiting to start or a claim action waiting to be executed.
-
event dispatchers - elements that distribute events from emitters to triggers. Each layout contains a built-in dispatcher called the default dispatcher. Unless instructed otherwise, events are always sent to the default dispatcher.
Events themselves fall into three categories:
-
system events - events sent by editor itself; for example, the
loadevent sent when a layout is created or an event sent upon receiving a server response. The user cannot add or send system events, and their names are fixed. System events can carry additional data, such as theresumeproperty of theloadevent. These properties can be used in event triggers to more precisely specify which event should activate them. -
named events - user-defined events identified by a name, for example
button_clickedormy_event_100. Named events can be sent from user-created event emitters, but their names must first be defined in editor settings. Named events do not carry any additional data apart from their name. -
numeric events - events identified only by a number. Numeric events can be sent from user-created event emitters and do not need to be defined anywhere. Using named events is recommended in most cases to avoid the problem of "magic numbers": it is easier to understand what
popup_opendoes than what123does. Numeric events do not carry any additional data apart from their ID.
Variable System
Editor's variable system allows the game to store information about its state and configuration. Variables can be accessed and used in multiple ways, but most often they are used in event trigger conditions to further define when a trigger should activate.
For example, an event trigger can be configured to wait for the named event
popup_openbut activate only when the variablegame.finishedis set. In this case, the trigger condition would begame.finished == 1.
All editor variables are numeric only, meaning their values can only be numbers (or numeric arrays).
Variables are split into the following groups:
-
system variables - built-in variables that can only be read by the user. For example,
game.mutereads0when sound is on and1when sound is off. The list of system variables is mechanically defined and cannot be modified by the user. -
constant variables - variables that never change value. They are used to reference fixed numeric values; for example,
pican be set to3.14. Constant variables can be user-defined in editor settings, but many of them are built-in, such asprize.*,text.*,asset.*, orstate.*variables. -
global variables - user-defined variables that can be read and written. They are defined in editor settings and can be used to perform various tasks. For example, a button can remember if it was clicked and a popup can remember if it was opened. Note that global variable values are not preserved between game loads.
-
local variables - user-defined variables that can be read and written, but their values are stored not globally but on an event dispatcher, meaning each event dispatcher can have its own set of local variable values.
Let's say there are 10 popup layouts on screen and each popup wants to remember whether it was opened or not. This can be easily achieved by using a single local variable, as each popup layout will store a separate value for that variable in its own default dispatcher.
Expressions
Expressions allow the computation of complex values during runtime using standard arithmetic and logical operators. All editor variables can be referenced in expressions.
Logical operators include:
!a(not)a == b(equal)a != b(not equal)a > b(greater)a >= b(greater or equal)a < b(less)a <= b(less or equal)a && b(and)a || b(or)
All logical operators resolve to 0 when false and 1 when true.
All non-zero values are considered true, so
!2,!-1, and!0.01all evaluate to0.
Arithmetic operators include:
a + b(addition)a - b(subtraction)a * b(multiplication)a / b(division)a % b(modulo)
Additionally, the following syntax is supported:
a ? b : c(ternary operator / short if)a[b](array indexing)a(b, c, d)(function call)a ? b -> c, d -> e, f(switch-case)
If the above four items are alien to you, ignore them for now. They are listed here to show people with basic programming knowledge what syntax is supported.
Text System
The text system allows the user to reference and manipulate text values. All texts loaded in the game can be split into three groups:
-
static texts - user-defined texts that are loaded on game start and remain unchanged throughout the game. Static texts can be defined in editor settings.
-
dynamic texts - texts whose values can change during gameplay, most often after a server response. Most dynamic texts are user-defined, but some are built-in, for example the
error_messagetext containing the error message when an error occurs. User-defined dynamic texts can be defined in editor settings. -
runtime texts - texts without an assigned name, created during game runtime. These texts are not defined by the user and cannot be referenced directly. The most common use case is sending prize names.
For example, a system variable
paytable.namescan contain an array with IDs referencing runtime texts, one for each prize. This data can be used to display a paytable.
Texts can be referenced by name (excluding runtime texts) or by ID. Text IDs can be accessed via the built-in text.* constants; for example, a static text button_label will have its ID stored in a constant named text.button_label.
Text Templates
Text templates allow the user to combine text values during game runtime. The most commonly used features include:
-
referencing texts by name -
foo {bar}will replace{bar}with the value of a dynamic or static text namedbar. -
evaluating expressions -
foo {(bar + 1)}will replace{(bar + 1)}with the value of the expressionbar + 1. -
resolving text IDs -
foo {@(bar + 1)}will replace{@(bar + 1)}with the text whose ID matches the value of the expressionbar + 1.
A common pattern is to use templates such as
{@(text.weekday_1 + local.id)}. Let’s break it down:local.idis a local variable namedid, andtext.weekday_1is the ID of a static text namedweekday_1. Assuming textsweekday_2,weekday_3, etc. are defined directly afterweekday_1, the expressiontext.weekday_1 + local.idwill resolve to the ID ofweekday_1whenlocal.idis 0,weekday_2whenlocal.idis 1, and so on. Then@resolves the text ID back to a text value. Thanks to this trick, we can convert a number (local.id) into a weekday name (weekday_x).
Standard Layer Properties
Many layer properties are shared between different layer types. This section will cover the most common properties.
Enable
Almost all layers include the enable checkbox in their properties. Disabling a layer (unchecking the checkbox) is functionally almost identical to removing it. Most common use-cases for disabling layers are:
- leaving optional functionality that is disabled by default but can be easily enabled later
- disabling unwanted inherited layers in inherited layouts (removing inherited layers is not allowed)
- temporary disabling some functionality
Enable state can only be changed manually, no layer can read or write this property.
Label
The label property allows to set the layer name displayed in the editor layer panel. This serves no other function, the name is not used for anything else.
Correctly naming layers is crucial for keeping the layout clean and easy to modify. Some recommendations for when naming layers:
- split words with spaces and capitalize first letter of each work:
One Two Three - include layer type or part of layer type at the end for the name:
Background Image,Open Animation, etc - avoid very long names
- do not repeated parent's name: for example inside parent
Play Buttonname a text layerLabel Textand notPlay Button Label Text
Positioning
Layer positions are always calculated in relation to parent layer. If layer is a top-level layer then position is calculated in relation to the viewport.
Note that this is different from how a lot of graphic software work where all positions are in relation to viewport.
For a HTML analogy assume all elements on screen have
position: relativeset.
Three different positioning modes are supported, each adding more control then the previous:
- simple - allows setting three basic properties
Top- vertical position of element in pixels, calculated from the top corner of the parent layerLeft- horizontal position of the element in pixels, calculated from the left corner of the parent layerZ Index- by default the order in which layers are drawn on screen is set by the layer order, the lower the layer, the later it is drawn.Z Indexallows to change this behavior in scope of current parent, layers with higherZ Indexwill be drawn later then layers with lowerZ Index. It is not recommended to useZ Indexif the same effect can be achieved by changing the order of layers.
- relative - adds two additional properties allowing for positioning in relation to parent layer size
X Origin- any value between0and1, changes the point from whichLeftposition is calculated.0is left parent corner (same as in simple mode),1is right parent corner and0.5is parent center.Y Origin- same asX Originbut forTopwith0being parent top corner and1being parent bottom corner.
- complex - allows the most control by adding even more properties
X Anchor- changes from which point inside the current layer theLeftposition is calculated. In the two previous modesX Anchoris set to0.5meaning from the center of the layer. This can be adjusted to any value between0(left corner) and1(right corner).Y Anchor- same asX Anchorbut fromToppositionX Pivot- together withY Pivotsets where the rotation axis is located inside the current layer. In the two previous modesX Pivotis set to0.5meaning from the center of the layer. This can be adjusted to any value between0(left corner) and1(right corner).Y Pivot- seeX PivotRotation- layer rotation in degreesFlipped- allows to mirror the layer vertically or horizontallyX Fill- available on layers with settable width, adds to layer width a percentage of the parent layer's width,1=100%.Y Fill- available on layers with settable height, adds to layer height a percentage of the parent layer's height,1=100%.Render Barrier Id- allows deferring rendering of current layer until aRender Barrierlayer is rendered with matching id. This allows for more complex draw order changes. Setting to0disables this feature (layer gets rendered normally). Read more in theRender Barrierlayer description.
The simplest positioning mode realizing what is needed should be always selected. Do not set mode to relative or complex when simple is enough.
Layer ID
Each editor layer has it's own unique ID (for example LwbDlP5qlxPfDHA3). These IDs can be accessed in editor in two ways:
- by opening the layer properties, it's always they first property in property panel
- by using the
Copy Layer Idoption from the layer panel context menu
IDs are needed each time when a layer configuration requires the user to reference some other layer. For example when using a property accessor layer specifying which layer's properties should be accessed is required.
Only layers from current layout can be referenced by ID.
Additionally two "special" layer IDs can be used:
parent- always references the parent layer's IDroot- references the layout's root container
Event Triggers
Each layer supporting triggers will have the following property:
Dispatcher- layer ID of dispatcher to register on, when left blank the layout's default dispatcher is used. This can reference anydispatcherlayer but also a few other layer types that have their own dispatchers, mainly thecomponentlayer. The same dispatcher is shared between all triggers available on a single layer.
As well the following properties per supported trigger:
Event- selects the type of event the trigger should activate on- to use a Named Event first select
namedEventoption and then select the event name to use in theEvent Namefield below - to use a Numeric Event first select
numericEventoption and then provide the event number in theEvent Idfield below
- to use a Named Event first select
Condition- optional expression that is checked when a matching event is found, the trigger gets activated only if the condition results in a non-zero (true) value. Each trigger has its own condition. When resolving condition expressions local variables are read from the configured dispatcher.
Event Emitters
Each layer supporting emitting events will have the following properties per emitted event:
Event- event type to be sent, only Named Event and Numeric Event can be sent from user controlled event emitters.Event Name/Event Id- name or number of event to be sent, depending on selected event typeTarget Dispatcher- layer ID of dispatcher that the event is to be sent to, when left blank the current layout default dispatcher is used.Broadcast- when settarget dispatcheris ignored and the event is sent to current screen layout's default dispatcher instead.
Broadcastuse example - lets assume the currently active screen layer is called "main" and it has a layout named "popup" inside in. The player pressed "close" button inside the popup and now the "popup" layout wants to send a event back to the "main" layout. Sending the event to the default dispatcher will deliver the event to all layers inside the "popup" layout, but not "main". SettingTarget Dispatcherwill not help as only layers inside current layout can be referenced by ID. Solution it to useBroadcast- the event will be sent to the current screen layout's default dispatcher, so "main" in this scenario.
Text Sources
Multiple layer types take text as input, most obviously the text layer, but also multiple other, like redirect or postMessage. While the layers are different, a common interface is used for configuring the input text:
Source- where should the text be read fromstatic- static texts should be useddynamic- dynamic texts should be usedcustom- a custom value will be provided by the user
Static Text Id/Dynamic Text Id- text to use when source was set tostaticordynamicCustom Value- whencustomis selected the value to be used for the text. This field fully supports Text Templates.With Override- when set allows user to also provide an override text. Override text will be displayed while in editor instead of the actual text value. Override value is present only in editor.Override- override text value, does not support Text TemplatesWeak Override- when set the override will be applied only while the layer's layout is active in editor, available only whenWith Overrideis set.
Custom Valueexample use case - button label should set tolabel_unclaimedorlabel_claimedbased on claim status, in such case custom text can be set to{@(game.claimed ? text.label_unclaimed : text.label_claimed)}
With Overrideexample use case - popup textprize_textis configured as an dynamic text with no default value. To show something in editor so that the text style and position can be properly configured override is set with some placeholder text.
Build-in layers
A few layers a built-in layers that are always present and can not be added or removed by the user. These layers are always on the very top of the layer panel.
Demo layer
Demo layer is present in all layouts but includes special functionality in screen layouts. It allows for testing the game without leaving the editor.
Demo layers always contain the following buttons:
Reset- clear all variables and return layout to its initial stateReset + Init- preform a reset and initialize all layersLoad- preform a reset, initialize all layers and send aloadevent withresumeset to falseLoad (true)- preform a reset, initialize all layers and sends aloadevent withresumeset to true
Note: in game
resumeon theloadevent is set when a game is re-loaded due to a orientation change
As well as the following sections:
- Variable Viewer - allows to view and modify all editor variables
- supports reading and writing to all variable types
Dispatchercan be provided to target local variables of a specific dispatcher (default dispatcher is used when omitted)Value Modecan be changed toarrayto set an array value
- Layer Finder - quickly jump to a layer in current layout by providing its ID
- Event Builders - send any event to currently opened layout, including system events
Event Builder Countsets the amount of event builders to display- each event builder functions as a separate event emitter
Demo layers of screen layouts contain additional controls allowing the user to set load state as well as preform user actions.
As each mechanic has different game logic these controls differ between mechanics. To explain load state and user actions OTB (open the box) will be used as an example. Keep in mind that other mechanics will have different actions and different state properties.
Load state determines in what state should the game be after load - is the player opening a fresh new game or maybe he is returning to an already played game? If so what box did he open? What did he win? Did he claim his prize? All this is set by the following properties:
With State- when unchecked game has no load state - its a clean new gamePrize Reference- the prize reference of the won prizeBox Reference- sets which box was openedClaimed- sets if the prize was claimedAccepted- sets if the prize was accepted (when using accepted / decline functionality)
User actions allow to "play" the game from within editor by preforming actions players would normally preform while playing the game. Each action requires the game to be in the correct state, for example in OTB the Open action can only be issued when the game has not been played and Claim action only after a box was opened. Pressing the action button in a incorrect state will display an error message on screen.
In OTB following actions are available:
-
Open- simulate opening a boxNetwork Delay- delay in milliseconds simulating the time it takes to communicate with the serverBox Reference- which box to openPrize Reference- which prize to win
-
Claim- simulate claiming a prizeNetwork Delay- delay in milliseconds simulating the time it takes to communicate with the serverAccepted- did the player accept the prize (when using accepted / decline functionality)
Note: user actions are not "magic", they require a bit of consideration to function correctly. All they do is emit server request / response system events and update system variable values. If clicking on a box emits a "box_clicked" named event that starts an animation and triggers the server request then the animation will not be started when using the open action from within editor. The correct approach would be to start the animation on the server request event and have "box_clicked" only trigger the server request. Both configurations function the same while playing the game, but only the second will allow the game to be tested in editor with user actions.
Viewport layer
Viewport layer is present only in screen layers and controls how the layout is displayed on screen. It also controls which screen layout should be chosen at a given time.
Viewport Size
The in-game layout dimensions are determined by the viewport size and the game window dimensions. The whole process can be explained in the following steps:
- viewport size is modified to match the game window aspect ratio
- modified viewport size is used as layout dimensions (these are the dimensions "seen" from inside the layout)
- the whole game is scaled to have the layout fill the game window
To match the game window aspect ratio, one of two operations has to be preformed:
- expand - viewport is expanded on one dimension to match the game window aspect ratio
- crop - viewport is cropped on one dimension to match the game window aspect ratio
Using a html analogy
object-fit: containwould be expand andobject-fit: covercrop
Viewport size can be configured using one of two different modes:
simple- two viewport size values are provided:widthandheight. To match the game window aspect ratio the viewport size is always expanded.min-max- four viewport size values are provided:minWidth,maxWidth,minHeightandmaxHeight.- viewport determined by
maxWidthandmaxHeightis cropped to match the game window aspect ratio as long as the resulting dimensions are greater thenminWidthandminHeight - otherwise the viewport is expanded using
minWidthorminHeightas one of the final dimensions andmaxWidthormaxHeightas one of the expanded dimensions.
- viewport determined by
Notice that when
maxWidth = minWidthandmaxHeight = minHeightthenmin-maxmode always expands (behaves likesimplemode) and whenminWidth = 0andminHeight = 0then it always crops.
If viewport size is set to width = 0 and height = 0 then the layout size matches the game window size directly and no adjustment or scaling is preformed.
Example:
- game window: 1200x800
- viewport size: 1920x1080 (simple mode)
- layout size: 1920x1280 (expanded vertically)
Tip: when starting work on a new layout its recommended to set the viewport size to match the design. This will make layout work much easier and the final viewport size adjustment can be done later.
Load Condition
Load condition is an expression controlling which screen layout should be loaded after the game loads. It also decides if the currently loaded layout should be replaced when the game window size changes.
Most common use case of load conditions is switching from landscape to portrait layouts after an orientation change.
To find which layout to use, the load condition of each screen layout is evaluated in order the layouts where defined in editor settings. The first layout which load condition resolved to a true value is selected. If nether evaluated to a true value the last in order is used.
The layout conditions are evaluated on game load and when game window changes size. If the resulting layout is the same layout that is currently loaded nothing happens, otherwise the layout is changed.
The following system variables are most often used in layout load conditions:
window.width- current game window width in pixelswindow.height- current game window height in pixelsgame.mobile- 1 if game is running on a mobile device, 0 otherwise
Drag Event Capture
By default the game does not process drag events to not interfere with the host site scrolling. In some cases drag events are needed, for example the scrollableArea layer requires them for touch scroll to function.
If drag event processing is enabled and the game window is iframed such that there is content above or below it then players will not be able to scroll the web page above or below the game window via touch events.
If it is fine for the game window to process drag events, then the Capture Darg Events checkbox can be checked on the viewport layer to enable them for the whole layout.
If drag event processing is needed only for a portion of screen then the
noScrollZonelayer can be used instead. This way the parent page will remain scrollable while interacting with content outside the defined space.
Preview Configuration
Viewport layer also contains properties that control how they layout should be displayed in editor. These properties do not affect the layout itself, just how it behaves when shown in editor. These properties include:
mask- should elements extending beyond the layout boundaries be clipped. Note that while in game such a thing is not possible as layout boundaries are at the same time the game window boundaries.Preview Aspect Ratios- what options should be shown in the "Preview Window" select box in the editor toolbar. Used to define preview aspect ratios presets accessible from the toolbar.
Root layer
Root layer is the parent layer of all user created layers.
In screen layouts it has no properties. It's used only as an anchor point for the user to create top-level layers on.
In non-screen layouts its contains standard position properties. These properties set the dimensions and position of the layout while it is selected as the active layout in editor. They are not used when displaying the layout via a component layer - the component layer position properties are used instead.
For newly created non-screen layouts it is recommended to always set positioning mode of the root layer to
relative- this will ensure the layout content is displayed in the center of the editor window, not requiring the user to pan to bring it into view.
Player Interaction
Player interaction allows the game react to player input. Only interactive layers can be interacted with. A layer becomes interactive once the interaction layer is added to it as a child layer. Not all layers can be interactive, for example a sound layer can not become interactive. In general only layers that represent an element on screen can become interactive.
Note that some layers (like
button) can make a layer interactive without the need of adding a separateinteractionlayer.
Interactive layers can react to the following user actions:
- pointer tap - element has been clicked (pressed and released)
- pointer down - element has been pressed
- pointer up - element has been released
- pointer enter - cursor has been moved above the element
- pointer leave - cursor has been moved away from the element
Each of the above actions can have a separate event emitter defined on the interaction layer, allowing events to be sent once a given action occurs. The layer is still interactive even if all event emitter are disabled.
If multiple interactive layers are placed on top of each other, then only the top most layer will be activated. In other words only the most recently drawn layer currently under the cursor will be activated.
This is often use to create so called "interaction blockers" - empty groups spanning the whole screen with an
interactionlayer added to them. Such group will capture all player actions that would normally activate layers behind it. This, for example, is useful for popups - to temporary block the player from interacting with anything outside the popup.
Few additional things have to be considered if an interactive layer has interactive child layers.
- for the child to be activated the interactive parent must also be activated, this means that both child and parent must be under the cursor at the same time. In other words if an interaction layer is added to a group without dimensions, then its child layers will never be able to be activated, regardless of their interaction state.
- both parent and child will be activated, in order from child to parent.
- disabling the interaction layer on the parent will not disable the interaction layer on the child
Note that events in html work very differently. Expecting things to follow the same rules here is a mistake. The best html analogy that can be made is setting
pointer-events: noneby default on all elements and then overriding it withpointer-events: autoon interactive elements.
Using layouts
Avoiding repetition is one of the most important things to consider. For example lets say a grid of 80 boxes is needed. One can painstakingly create 80 almost identical copies of the same layers defining the box.
It will work, given no user error was made on the way of the monotonous clicking, but now imagine something has to be changed. The change has to be repeated 80 times, and only after all that work can it be tested if the change was actually needed...
This section describes tool that can be utilized to avoid manual repetition.
Layout Inheritance
Often there is a need to create two variants of the same layout. Most common example would be a landscape and portrait version of the screen layout.
One solution would be to simply create one layout, copy it, and then modify the copy to create the second layout. But this creates a problem - whenever we change something that was common to both layouts we have to change it in two places. Manually keeping both layouts in sync with each other is not simple and can quickly lead to bugs.
Layout inheritance solves this problem by allowing the user to link two (or more) layouts together and keep them synchronized automatically.
To use layout inheritance first create the first layout (the parent layout). Then, instead of copying it, configure the second layout to inherit from the first layout. This will transfer all layers (and their properties) from the parent to the child with the following rules applied:
- when a layer property is modified in the child, then it becomes marked in blue as diverging from the parent
- when a layer property is modified in the parent, then it also updates in the child as long as that property was not already diverging (marked blue)
- when a layers is added to the child, then it and all of its properties get marked in blue
- when a layer is added to the parent, then it is also added to the child
- inherited layers can not be removed or moved in the child, only the added "blue" layers can
- when a layer is removed or moved in the parent, then it also is removed or moved in the child
Tip: to "remove" an unwanted inherited layer simply disable it by unchecking its
Enabledcheckbox. To "move" an inherited layer theZ Indexproperty can be used.
Non-Screen Layouts
Technically speaking non-screen layouts are not needed, the full game can be built entirely from layers on a single screen layout. Although doing so can quickly prove to be troublesome.
Non-screen layouts are most often used to:
- move a self-contained portion of the game to a non-screen layout to simplify the screen layout. Popups, for example, are usually moved to separate layouts.
- repeat a portion of the game multiple times - for example a paytable can have 6 rows, each being the same non-screen layout instance.
In most cases when creating multiple copies of a single layout it is necessary to have each copy slightly different. For example, each paytable item has to reference a different prize. This is usually done with the help of local variables. Each layout instance will have its own default event dispatcher with its own set of local variables.
Initial values for local variables can be set when creating a layout instance via the component layer configuration. Each instance then have different values and be then used in expressions to select the correct text, image, delay, animation, etc.
It is recommended to name the local variable used to differentiate component instances
id.
Another way to differentiate layout instances is to use layout inheritance. Doing so requires creating a separate layout per variant which quickly gets out of hand if the amount of required variant is high. This is not an issue if only a few variants are needed. For example usually a no-win popup is a layout that inherits from the win popup.
Loader
Loader is a special screen layout type that is present on the screen while the game is loading. At least one loader screen layout has to exist for the game to work.
The loader has to deal with a unique issue - game assets are loading yet we have to somehow show game assets on the screen. For images this is partially solved by the special loaderImage layer type that allows using not yet loaded images, but other asset types require manual handling of one sort or another.
Load Priority
Game assets are loaded in groups, a system event loaderAssetStep is emitted each time a group is fully loaded. The groups are determined by each asset's load priority - assets with equal load priority are grouped together. Load priority also sets the order in which the groups are loaded - the higher the priority the sooner will the group be loaded. This allows to plan the order in which assets will be loaded, prioritizing assets needed for the loader itself.
Usually the highest priority is assigned to assets needed for progress bar and the game logo, then the loader background and only then the rest of the game. This is to show the progress bar and logo as soon as possible - progress bar shows to the player that something is loading and logo what is loading.
The priority of the last loaded asset group can be accessed via a system variable loader.priority. The initial value of this variable is set to Infinity.
Note that
Infinityis not a valid expression value, to check for it compare with large number, for exampleloader.priority > 1000.
Layer Support
Not all layer types are supported in the loader layout. Most layers to require a asset to function are not available (for example sound or video).
That said, there is a workaround - they can added to a non-screen layout and then that layout can be included in loader via a component layer. When doing so it must be made sure that the component is set to dynamic mode and created only after the required assets are fully loaded. If this is not ensured the game will fail to load and show an error on screen.
Exiting The Loader
Decision for when to close the loader is left up to the user. Only requirement is that all asset have to be fully loaded before the loader is closed.
Most commonly the loader is configured to either close automatically after finishing loading or to close after a "start" button press.
The "close loader" action is issued by an event trigger on a dedicated layer type called loaderAction. Adding and configuring this layer is mandatory as without it the loader will never exit.
To check the current load progress the loader.progress variable can be read. It represents the current loading completion percentage, with 100 read when loading is finished. After each update of this variable a system event loadProgress is emitted.
The simplest functional loader layout consists of a sole
loaderActionlayer configured to trigger onloadProgresssystem event with trigger condition set toloader.progress == 100
Text and Fonts
Three types of text layers are supported, each with its with its own pros and cons:
text- no rich text support, works with standard fontshtml- low performance, full html support, displayed as an overlay over the gamesdfText- high performance, rich text support, requires special pre-computed assets to function
Each of these layers are described in detail in the layer type glossary, this section will only focus on their differences and use cases.
Most setups use only the text layer as its the simplest to use and has acceptable performance. Its main weakness is no rich text support - each text layer has to have a single text style. Issues might also start arising if many (over 40-60) text layers are on screen at once and even faster if their text contents changes each frame.
Under the hood the
textlayer uses html canvas api to render each text on a separate in-memory texture
The html layer is used mainly to display big chunks of formatted (most often scrollable) text. While capable of displaying any html content it comes with many caveats:
- the displayed html content is not part of the game itself - it will be displayed on top of the game. This means no game element can be displayed in front of the
htmllayer (except for anotherhtmllayer). - it requires css code to be added inside layer properties
- while it can be animated and manipulated like any other layer, performance can be lacking on low-end devices
- having more then one
htmllayer on screen at once is not recommended - it can behave slightly differently on different browsers / devices
Under the hood the
htmllayer creates a div element on top of the game's canvas and each frame updates its transform to match the in-game layer
The sdfText layer offers the highest performance allowing for hundreds of text layers to be displayed on the screen, updated as often as needed. It also supports rich text allowing for mixing of multiple styles in a single text layer. These capabilities come with a cost - it requires special font files to function as well as some additional extra considerations. The special font files can be created from within asset section in our backoffice, but most commercial font licenses disallow creating derivative work and this can be seen as such. Using SIL licensed fonts (for example most google fonts) is fine.
Under the hood the
sdfTextlayer uses a dedicated shader that uses pre-computed singed distance fields stored inside the font asset to render vector quality fonts using raster textures.
To choose the right text layer for the job the following rule of thumb can be used:
- if scrollable text is needed, for example for terms and conditions, use
html - if the game extensively uses a lot of texts and updates them often consider using
sdfText - in all other cases use
text
Frame by Frame Animations
Frame by frame animations are sets of images played in sequence (often called "gifs" not actually having anything to do with the gif file format). While this type of animations are supported, not every sequence of imagery can be used in game. Theres are size constraints to be considered.
During upload each animation frames gets trimmed and packed side by side on 2048x2048 images. There is a limit of how many of such images a mobile device can load. The images can also end up being quite big (even after compression) resulting in longer game load times. The recommended maximum amount of such textures is 10. If all animation frames do not fit on less then 10 textures then the frames should be downscaled or the amount of framed reduced.
Animations should be uploaded as lossless webp files. Our backoffice's asset section offers a tool to convert a sequence of png images into such a file. Usage of our tool is recommended as it ensures the resulting animation is created as a lossless file. Creating a lossy webp files will result in degraded quality with no benefit - the uploaded file is not directly used by the game, it gets re-encoded and re-compressed by our asset pipeline.
The animations can be played by adding an animation layer to an image layer that has the webp file selected as its image asset. See the animation layer in the layer type glossary for more information.
Videos
Configuration
Assets
Texts
Variables
Events
Error handling
Event Distribution
Understanding the order in which emitted events are be processed is key when working with editor. Not being fully aware of how events are distributed can lead to one being surprised by smilingly undefined behavior.
An event dispatcher forwards received events to recipients that are registered on it. A single event is send to all recipients in order they where registered in the dispatcher. A single recipient can be either a layer or another dispatcher.
Event will be sent to the next recipient only after current recipient fully finished processing the event.
Consider the following recipients in a dispatcher:
layer 1layer 2dispatcher alayer blayer c
layer 3layer 4
Layers b and c in the above example are recipients of dispatcher a.
The order in which events will be received by individual recipients is as follows:
layer 1layer 2dispatcher alayer blayer clayer 3layer 4
Events can be emitted even while processing an event. For example a layer can be configured to emit event x immediately upon receiving event y. To have the order of processing well-defined in such situations the following logic is used:
- if an event is being sent to a dispatcher that is currently not processing any events the event is sent as normal (immediately)
- if the dispatcher is processing events then the event gets added to the dispatcher's incoming event queue
- after the dispatcher finishes processing it checks it's queue - if it's not empty it immediately starts processing the first queued event
Lets expand our example. We are sending event e0 to a dispatcher with the following recipients:
layer 1layer 2(sends evente1upon receivinge0)dispatcher alayer b(sends evente2upon receivinge0)layer c(sends evente3todispatcher aupon receivinge0)layer d
layer 3(sends evente4upon receivinge0)layer 4
The order in which events will be sent to layers is as follows:
e0->layer 1e0->layer 2(e1added to queue)e0->layer b(e2added to queue)e0->layer c(e3added todispatcher aqueue)e0->layer d(dispatcher afinishes processing,e3is on top of it's queue)e3->layer be3->layer ce3->layer d(dispatcher afinishes processing, it's queue is empty)e0->layer 3(e4added to queue)e0->layer 4(main dispatcher finishes processing,e1is on top of it's queue)e1->layer 1e1->layer 2e1->layer be1->layer ce1->layer d(dispatcher afinishes processing, it's queue is empty)e1->layer 3e1->layer 4(main dispatcher finishes processing,e2is on top of it's queue)e2->layer 1e2->layer 2e2->layer be2->layer ce2->layer d(dispatcher afinishes processing, it's queue is empty)e2->layer 3e2->layer 4(main dispatcher finishes processing,e4is on top of it's queue)e4->layer 1e4->layer 2e4->layer be4->layer ce4->layer d(dispatcher afinishes processing, it's queue is empty)e4->layer 3e4->layer 4(main dispatcher finishes processing, it's queue is empty)
Layer Type Glossary
Variable and Function Glossary
Updated about 9 hours ago